﻿using DevExpress.LookAndFeel;
using DevExpress.Utils.Drawing;
using DevExpress.XtraBars;
using DevExpress.XtraBars.Ribbon;
using DevExpress.XtraBars.Ribbon.ViewInfo;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using XCI.Core;
using XCI.Extensions;
using XCI.Helper;
using XCI.Service;

namespace XCI.Windows
{
    /// <summary>
    /// 图片批量上传控件
    /// </summary>
    public partial class GalleryImageUploadControl : BaseUserControl
    {
        private readonly List<GalleryImageFile> fileList = new List<GalleryImageFile>();
        public const string IMAGE_EXTENSION_NAME = "gif,jpg,jpeg,png,bmp";
        private Pen checkedBorderPen;
        private object _editValue;

        /// <summary>
        /// 初始化图片上传控件
        /// </summary>
        public GalleryImageUploadControl()
        {
            InitializeComponent();
            openFile.Filter = "PNG文件 (*.png)|*.png|GIF文件 (*.gif)|*.gif|JPEG文件 (*.jpg;*.jpeg)|*.jpg;*.jpeg|所有图像文件 |*.bmp;*.gif;*.jpg;*.jpeg;*.png;|所有文件 |*.*";
        }

        /// <summary>
        /// Bar控件
        /// </summary>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public BarManager BarManager => barManager;

        /// <summary>
        /// 工具条控件
        /// </summary>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public Bar ToolBar => bar1;

        /// <summary>
        ///图片控件
        /// </summary>
        [Category("XCI"), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public GalleryControl Gallery => galleryControl;

        /// <summary>
        /// 选择按钮
        /// </summary>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public BarButtonItem SelectButton => btnSelect;

        /// <summary>
        /// 移除按钮
        /// </summary>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public BarButtonItem RemoveButton => btnRemove;

        /// <summary>
        /// 上传按钮
        /// </summary>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public BarButtonItem UploadButton => btnUpload;

        /// <summary>
        /// 导出按钮
        /// </summary>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public BarButtonItem ExportButton => btnExport;

        /// <summary>
        /// 上传子文件夹
        /// </summary>
        [Category("XCI"), Description("上传子文件夹"), Browsable(true), DefaultValue(null)]
        public virtual string UploadFolder { get; set; }

        /// <summary>
        /// 移除文件时同时删除远程文件
        /// </summary>
        [Category("XCI"), Description("最多允许的文件数量"), DefaultValue(false)]
        public virtual bool AllowDeleteRemoteFile { get; set; } = false;

        /// <summary>
        /// 允许文件上传的扩展名,不含点,逗号隔开
        /// </summary>
        [Category("XCI"), Description("许文件上传的扩展名,不含点,逗号隔开"), DefaultValue(IMAGE_EXTENSION_NAME)]
        public string AllowFileExtensions { get; set; } = IMAGE_EXTENSION_NAME;

        /// <summary>
        /// 最多允许的文件数量
        /// </summary>
        [Category("XCI"), Description("最多允许的文件数量"), DefaultValue(99)]
        public int MaxFileCount { get; set; } = 99;

        /// <summary>
        /// 单个文件允许的最大值,单位KB,默认10M
        /// </summary>
        [Category("XCI"), Description("单个文件允许的最大值,单位KB,默认10M"), DefaultValue(10240)]
        public int SingleFileMaxSize { get; set; } = 10240;

        /// <summary>
        /// 是否显示工具栏
        /// </summary>
        [Category("XCI"), Description("是否显示工具栏"), DefaultValue(true), Browsable(true)]
        public virtual bool ToolbarVisable { get; set; } = true;

        /// <summary>
        /// 选择按钮是否可见
        /// </summary>
        [Category("XCI"), Description("选择按钮是否可见"), DefaultValue(true), Browsable(true)]
        public virtual bool SelectVisible { get; set; } = true;

        /// <summary>
        /// 选择按钮文本
        /// </summary>
        [Category("XCI"), Description("选择按钮文本"), DefaultValue("选择"), Browsable(true)]
        public virtual string SelectText { get; set; } = "选择";

        /// <summary>
        /// 移除按钮是否可见
        /// </summary>
        [Category("XCI"), Description("移除按钮是否可见"), DefaultValue(true), Browsable(true)]
        public virtual bool RemoveVisible { get; set; } = true;

        /// <summary>
        /// 移除按钮文本
        /// </summary>
        [Category("XCI"), Description("移除按钮文本"), DefaultValue("移除"), Browsable(true)]
        public virtual string RemoveText { get; set; } = "移除";

        /// <summary>
        /// 上传按钮是否可见
        /// </summary>
        [Category("XCI"), Description("上传按钮是否可见"), DefaultValue(true), Browsable(true)]
        public virtual bool UploadVisible { get; set; } = true;

        /// <summary>
        /// 上传按钮文本
        /// </summary>
        [Category("XCI"), Description("上传按钮文本"), DefaultValue("上传"), Browsable(true)]
        public virtual string UploadText { get; set; } = "上传";

        /// <summary>
        /// 导出按钮是否可见
        /// </summary>
        [Category("XCI"), Description("导出按钮是否可见"), DefaultValue(true), Browsable(true)]
        public virtual bool ExportVisible { get; set; } = true;

        /// <summary>
        /// 导出按钮文本
        /// </summary>
        [Category("XCI"), Description("导出按钮文本"), DefaultValue("导出"), Browsable(true)]
        public virtual string ExportText { get; set; } = "导出";

        /// <summary>
        /// 获取或设置指定当前选定值。
        /// </summary>
        [Category("XCI"), Description("获取或设置指定当前选定值。"), Bindable(true), Browsable(false), DefaultValue(null)]
        public virtual object EditValue
        {
            get => _editValue;
            set
            {
                if (value.IsEmpty()) return;
                if (_editValue == value) return;

                _editValue = value;
                LoadImageAsync(_editValue.ToStringOrEmpty());
            }
        }

        /// <summary>
        /// 是否有图片
        /// </summary>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool HasImage => fileList.IsListNotEmpty();

        /// <summary>
        /// 是否需要上传
        /// </summary>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool RequiredUpload
        {
            get
            {
                return fileList.Any(p => !p.IsUpload);
            }
        }

        /// <summary>
        /// 图片加载前事件
        /// </summary>
        [Category("XCI"), Description("图片加载前事件")]
        public event EventHandler<ImageBeforeEventArgs> BeforeLoad;

        /// <summary>
        /// 图片加载之后事件
        /// </summary>
        [Category("XCI"), Description("图片加载之后事件")]
        public event EventHandler<EventArgs> AfterLoad;

        /// <summary>
        /// 图片上传前事件
        /// </summary>
        [Category("XCI"), Description("图片上传前事件")]
        public event EventHandler<ImageBeforeEventArgs> BeforeUpload;

        /// <summary>
        /// 图片上传之后事件
        /// </summary>
        [Category("XCI"), Description("图片上传之后事件")]
        public event EventHandler<GalleryImageUploadEventArgs> AfterUpload;

        /// <summary>
        /// 当图片加载前触发
        /// </summary>
        [EditorBrowsable(EditorBrowsableState.Advanced)]
        protected virtual void OnBeforeLoad(ImageBeforeEventArgs e)
        {
            BeforeLoad?.Invoke(this, e);
        }

        /// <summary>
        /// 当图片加载后触发
        /// </summary>
        [EditorBrowsable(EditorBrowsableState.Advanced)]
        protected virtual void OnAfterLoad(EventArgs e)
        {
            AfterLoad?.Invoke(this, e);
        }

        /// <summary>
        /// 当图片上传前触发
        /// </summary>
        [EditorBrowsable(EditorBrowsableState.Advanced)]
        protected virtual void OnBeforeUpload(ImageBeforeEventArgs e)
        {
            BeforeUpload?.Invoke(this, e);
        }

        /// <summary>
        /// 当图片上传后触发
        /// </summary>
        [EditorBrowsable(EditorBrowsableState.Advanced)]
        protected virtual void OnAfterUpload(GalleryImageUploadEventArgs e)
        {
            AfterUpload?.Invoke(this, e);
        }

        /// <summary>
        /// 设置按钮状态
        /// </summary>
        protected virtual void SetButtonStatus()
        {
            btnRemove.Enabled = galleryControl.Gallery.GetCheckedItemsCount() > 0;
            btnUpload.Enabled = RequiredUpload;
            btnExport.Enabled = HasImage;
        }

        /// <summary>
        /// 加载图片
        /// </summary>
        /// <param name="paths">图片路径(逗号分割)（服务器路径）</param>
        public virtual void LoadImageAsync(string paths)
        {
            if (string.IsNullOrEmpty(paths)) return;
            var args = new ImageBeforeEventArgs();
            OnBeforeLoad(args);
            if (args.Cancel)
            {
                return;
            }

            var loadMask = new LoadingMask(this);
            try
            {
                loadMask.Show();
                fileList.Clear();
                var list = paths.SplitToArray();
                foreach (var path in list)
                {
                    var imageFile = new GalleryImageFile
                    {
                        Name = WinFormHelper.GetRemoteFileName(path),
                        Path = path,
                        IsUpload = true
                    };
                    AddImageToGallery(imageFile);
                }
                SetButtonStatus();
                OnAfterLoad(EventArgs.Empty);
            }
            finally
            {
                loadMask.Hide();
            }
        }

        /// <summary>
        /// 选择图片
        /// </summary>
        public virtual void SelectImage()
        {
            this.openFile.FilterIndex = this.openFile.Filter.Split('|').Length / 2 - 1;
            if (this.openFile.ShowDialog() != DialogResult.OK) return;
            if (this.openFile.FileNames.Length == 0) return;

            foreach (var filePath in openFile.FileNames)
            {
                if (!ImageHelper.IsImage(filePath))
                {
                    MessageBoxHelper.ShowWarn("请选择有效的图片文件");
                    return;
                }
            }

            foreach (var filePath in openFile.FileNames)
            {
                var info = new FileInfo(filePath);
                var imageFile = new GalleryImageFile
                {
                    Name = info.Name,
                    Path = filePath,
                    IsUpload = false
                };
                AddImageToGallery(imageFile);
            }

            SetButtonStatus();
        }

        private void AddImageToGallery(GalleryImageFile imageFile)
        {
            if (imageFile.Id.IsEmpty()) imageFile.Id = StringHelper.Guid();
            if (galleryControl.Gallery.Groups.Count == 0)
            {
                galleryControl.Gallery.Groups.Add(new GalleryItemGroup());
            }
            var group = galleryControl.Gallery.Groups[0];
            var item = new GalleryItem(null, imageFile.Name, null) { Value = imageFile };
            group.Items.Add(item);
            fileList.Add(imageFile);
        }

        /// <summary>
        /// 移除图片
        /// </summary>
        public virtual async Task RemoveImageAsync()
        {
            var items = galleryControl.Gallery.GetCheckedItems();
            if (items.Count == 0) return;
            if (MessageBoxHelper.ShowMessageYesNo("确定要移除选中的图片吗？") != DialogResult.Yes) return;

            foreach (var item in items)
            {
                var model = ToModel(item);
                if (AllowDeleteRemoteFile && WinFormHelper.IsRemoteFilePath(model.Path))
                {
                    await FileUploadService.Instance.DeleteAsResultAsync(model.Path);
                }
                fileList.RemoveIf(p => p.Id.Equals(model.Id));
                item.GalleryGroup.Items.Remove(item);
            }
            _editValue = fileList.Select(p => p.Path).JoinString();
            SetButtonStatus();
        }

        /// <summary>
        /// 上传图片
        /// </summary>
        public virtual async Task UploadImageAsync()
        {
            var args = new ImageBeforeEventArgs();
            OnBeforeUpload(args);
            if (args.Cancel) return;
            if (!RequiredUpload) return;

            foreach (var model in fileList)
            {
                if (model.IsUpload) continue;
                var uploadResult = await FileUploadService.Instance.UploadAsync(model.Path, UploadFolder);
                model.IsUpload = true;
                model.Path = uploadResult.Path;
            }
            _editValue = fileList.Select(p => p.Path).JoinString();
            SetButtonStatus();
            var ar = new GalleryImageUploadEventArgs { FileList = fileList };
            OnAfterUpload(ar);
        }

        /// <summary>
        /// 导出图片
        /// </summary>
        public virtual async Task ExportImageAsync()
        {
            List<GalleryItem> galleryList = galleryControl.Gallery.GetAllItems();
            if (galleryList.Count == 0) return;

            if (folderDialog.ShowDialog() != DialogResult.OK) return;
            var loadMask = new LoadingMask(this);
            try
            {
                loadMask.Show();
                foreach (var item in galleryList)
                {
                    var model = ToModel(item);
                    string destPath = Path.Combine(folderDialog.SelectedPath, model.Name);
                    if (WinFormHelper.IsRemoteFilePath(model.Path))
                    {
                        await FileUploadService.Instance.DownloadAsync(model.Path, destPath);
                    }
                    else
                    {
                        File.Copy(model.Path, destPath, true);
                    }
                }
                loadMask.Hide();
                MessageBoxHelper.ShowWarn($"导出成功,保存在{this.folderDialog.SelectedPath}");
            }
            finally
            {
                loadMask.Hide();
            }
        }

        private GalleryImageFile ToModel(GalleryItem galleryItem)
        {
            return galleryItem.Value as GalleryImageFile;
        }

        private async Task<Image> LoadImageCallback(string path)
        {
            if (WinFormHelper.IsRemoteFilePath(path))
            {
                var data = await FileUploadService.Instance.DownloadAsBytesAsync(path);
                return ImageHelper.FromArray(data);
            }

            return ImageHelper.LoadImage(path);
        }

        private void DrawMarkedIcon(GraphicsCache cache, Rectangle bounds)
        {
            RectangleF clipBounds = cache.Graphics.ClipBounds;
            RectangleF rect = cache.Graphics.ClipBounds;
            rect.Inflate(50, 50);
            cache.Graphics.SetClip(rect);
            //var drawRect = new Rectangle(bounds.X + (bounds.Width - 32) / 2, bounds.Y + (bounds.Height - 32) / 2, 32, 32);
            //var img = DevExpress.Images.ImageResourceCache.Default.GetImage("images/xaf/bo_validation_32x32.png");
            //cache.Graphics.DrawImage(img, drawRect);
            //cache.Graphics.DrawRectangle(new Pen(Color.Red), drawRect);//居中边框
            //cache.Graphics.DrawRectangle(checkedBorderPen, bounds);//图片边框
            cache.Graphics.SetClip(clipBounds);
        }

        private void Control_Load(object sender, EventArgs e)
        {
            bar1.Visible = ToolbarVisable;

            btnSelect.Caption = SelectText;
            btnSelect.Visibility = WinFormHelper.BarItemVisible(SelectVisible);

            btnRemove.Caption = RemoveText;
            btnRemove.Visibility = WinFormHelper.BarItemVisible(RemoveVisible);

            btnUpload.Caption = UploadText;
            btnUpload.Visibility = WinFormHelper.BarItemVisible(UploadVisible);

            btnExport.Caption = ExportText;
            btnExport.Visibility = WinFormHelper.BarItemVisible(ExportVisible);

            SetButtonStatus();
        }

        private void BtnSelect_ItemClick(object sender, ItemClickEventArgs e)
        {
            SelectImage();
        }

        private async void BtnRemove_ItemClick(object sender, ItemClickEventArgs e)
        {
            await RemoveImageAsync();
        }

        private async void BtnUpload_ItemClick(object sender, ItemClickEventArgs e)
        {
            await UploadImageAsync();
        }

        private async void BtnSave_ItemClick(object sender, ItemClickEventArgs e)
        {
            await ExportImageAsync();
        }

        private void GalleryControl_Gallery_ItemDoubleClick(object sender, GalleryItemClickEventArgs e)
        {
            var imagePreview = new FrmImagePreview(ToModel(e.Item).Path, fileList.Select(p => p.Path).ToList(), LoadImageCallback);
            imagePreview.Size = WinFormHelper.GetImagePreviewSize();
            imagePreview.Show();
        }

        private void GalleryControl_Gallery_GetThumbnailImage(object sender, DevExpress.XtraBars.Ribbon.Gallery.GalleryThumbnailImageEventArgs e)
        {
            if (e.Item.Value == null) return;

            var url = ToModel(e.Item).Path;
            using (Image thumbImg = AsyncHelper.RunSync(() => LoadImageCallback(url)))
            {
                e.ThumbnailImage = e.CreateThumbnailImage(thumbImg);
            }
        }

        private void GalleryControl_Gallery_ItemCheckedChanged(object sender, GalleryItemEventArgs e)
        {
            SetButtonStatus();
        }

        private void GalleryControl_Gallery_CustomDrawItemImage(object sender, GalleryItemCustomDrawEventArgs e)
        {
            if (!e.Item.Checked) return;
            var viewInfo = (GalleryItemViewInfo)e.ItemInfo;
            e.Cache.Graphics.DrawImage(e.Item.Image, viewInfo.ImageContentBounds);
            //DrawMarkedIcon(e.Cache, ((GalleryItemViewInfo)e.ItemInfo).ImageContentBounds);
            if (checkedBorderPen == null)
            {
                var isDarkSkin = DevExpress.Utils.Frames.FrameHelper.IsDarkSkin(UserLookAndFeel.Default);
                checkedBorderPen = isDarkSkin ? new Pen(Color.FromArgb(75, 75, 75), 3f) : new Pen(Color.FromArgb(200, 200, 200), 3f);
            }
            e.Cache.Graphics.DrawRectangle(checkedBorderPen, viewInfo.SelectedImageBounds);
            e.Handled = true;
        }
    }
}
